package rexsee.core.browser;

import java.io.File;
import java.io.FileInputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Stack;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import rexsee.core.application.RexseeApplication;
import rexsee.core.browser.clazz.ExtensionListener;
import rexsee.core.browser.clazz.FilterListener;
import rexsee.core.browser.clazz.JavascriptInterface;
import rexsee.core.browser.clazz.LogListeners.LogListener;
import rexsee.core.browser.clazz.UrlListener;
import rexsee.core.lang.RexseeLanguage;
import rexsee.core.utilities.Utilities;
import android.content.Context;
import android.net.Uri;

public class RexseeUrl implements JavascriptInterface {

	public static final String URI_MEDIA_IMAGE = "content://media/external/images/media";
	public static final String URI_MEDIA_VIDEO = "content://media/external/video/media";
	public static final String URI_MEDIA_AUDIO = "content://media/external/audio/media";

	public static String ERROR_FILE_NOT_FOUND = "File not found.";

	public static final String INTERFACE_NAME = "Url";

	@Override
	public String getInterfaceName() {
		return mBrowser.application.resources.prefix + INTERFACE_NAME;
	}
	@Override
	public JavascriptInterface getInheritInterface(Browser childBrowser) {
		return childBrowser.urlListeners;
	}
	@Override
	public JavascriptInterface getNewInterface(Browser childBrowser) {
		return new RexseeUrl(childBrowser);
	}

	public static final String ASSET_URL = "file:///android_asset/";

	public final ArrayList<UrlListener> listeners;
	public final ArrayList<ExtensionListener> extensionListeners;
	public final ArrayList<FilterListener> filterListeners;
	private final Browser mBrowser;

	private String syncContent = null;

	public RexseeUrl(Browser browser) {

		mBrowser = browser;
		listeners = new ArrayList<UrlListener>();
		extensionListeners = new ArrayList<ExtensionListener>();
		filterListeners = new ArrayList<FilterListener>();

	}

	public UrlListener get(String url) {
		UrlListener listener;
		for (int i = 0; i < listeners.size(); i++) {
			listener = listeners.get(i);
			if (url.toLowerCase().startsWith(listener.url)) { return listener; }
		}
		return null;
	}
	public void add(UrlListener listner) {
		listeners.add(listner);
	}
	public void run(String url, boolean addHistory) {
		if (url.replaceAll(" ", "").equals("")) return;
		Context context = mBrowser.getContext();
		if (mBrowser.history.getCurrentItem() != null) url = getAbsoluteUrl(url);
		UrlListener listener = get(url);
		if (listener != null) {
			if (!(url.toLowerCase().startsWith("http://") || url.toLowerCase().startsWith("https://")) && addHistory && listener.shouldAddToHistory(context, mBrowser, url)) {
				mBrowser.history.add(url);
			}
			listener.run(context, mBrowser, url);
		} else {
			if (url.startsWith(mBrowser.application.resources.prefix + ":")) {
			} else {
				mBrowser.pageBeforeStartListeners.run(mBrowser, url);
				mBrowser.function.loadHTML(url);
			}
		}
	}

	public static ExtensionListener getExtensionByUrl(Browser browser, String url) {
		for (int i = 0; i < browser.urlListeners.extensionListeners.size(); i++) {
			if (browser.urlListeners.extensionListeners.get(i).contains(url)) return browser.urlListeners.extensionListeners.get(i);
		}
		return null;
	}
	public ExtensionListener getExtensionByUrl(String url) {
		for (int i = extensionListeners.size() - 1; i >= 0; i--) {
			if (extensionListeners.get(i).contains(url)) return extensionListeners.get(i);
		}
		return null;
	}
	public void addExtension(ExtensionListener extensionListener) {
		extensionListeners.add(extensionListener);
	}

	public FilterListener getFilterByUrl(String url) {
		for (int i = 0; i < filterListeners.size(); i++) {
			if (filterListeners.get(i).containsUrl(url)) return filterListeners.get(i);
		}
		return null;
	}
	public void addFilter(FilterListener filterListener) {
		filterListeners.add(filterListener);
	}

	public void inherite() {
		if (mBrowser.parentBrowser == null) return;
		for (int i = 0; i < mBrowser.parentBrowser.urlListeners.listeners.size(); i++) {
			UrlListener tmp = mBrowser.parentBrowser.urlListeners.listeners.get(i);
			if (tmp != null) this.add(tmp);
		}
		for (int i = 0; i < mBrowser.parentBrowser.urlListeners.extensionListeners.size(); i++) {
			ExtensionListener tmp = mBrowser.parentBrowser.urlListeners.extensionListeners.get(i);
			if (tmp != null) this.addExtension(tmp);
		}
		for (int i = 0; i < mBrowser.parentBrowser.urlListeners.filterListeners.size(); i++) {
			FilterListener tmp = mBrowser.parentBrowser.urlListeners.filterListeners.get(i);
			if (tmp != null) this.addFilter(tmp);
		}
	}

	//Filter Operation

	public FilterListener getFilter(String type, String name) {
		for (int i = 0; i < filterListeners.size(); i++) {
			FilterListener listener = filterListeners.get(i);
			if (type != null && !type.equalsIgnoreCase(listener.type)) continue;
			if (name != null && !name.equals(filterListeners.get(i).name)) continue;
			return listener;
		}
		return null;
	}
	public void removeFilters(String type, String name) {
		for (int i = 0; i < filterListeners.size(); i++) {
			FilterListener listener = filterListeners.get(i);
			if (type != null && !type.equalsIgnoreCase(listener.type)) continue;
			if (name != null && !name.equals(filterListeners.get(i).name)) continue;
			filterListeners.remove(i);
		}
	}
	public String getFiltersByType(String type) {
		String rtn = "";
		for (int i = 0; i < filterListeners.size(); i++) {
			if (i > 0) rtn += ",";
			FilterListener listener = filterListeners.get(i);
			if (type != null && !type.equalsIgnoreCase(listener.type)) continue;
			rtn += "{";
			rtn += "\"name\":\"" + listener.name + "\"";
			rtn += ",\"type\":\"" + listener.type + "\"";
			rtn += "}";
		}
		return "[" + rtn + "]";
	}
	public int getFilterSizeByType(String type) {
		int rtn = 0;
		for (int i = 0; i < filterListeners.size(); i++) {
			FilterListener listener = filterListeners.get(i);
			if (type == null || type.equalsIgnoreCase(listener.type)) rtn++;
		}
		return rtn;
	}

	//Common operation

	public String getContent(final String url) {
		return getContentWithEncoding(url, mBrowser.application.getDefaultEncoding());
	}
	public String getContentWithEncoding(final String url, final String encoding) {
		Uri uri = Uri.parse(url);
		if (uri == null) return null;
		String scheme = uri.getScheme();
		if (scheme == null) return null;
		scheme = scheme.toLowerCase();
		if (scheme.equals("http") || scheme.equals("https")) {
			syncContent = null;
			mBrowser.progressDialog.show(RexseeLanguage.PROGRESS_ONGOING);
			new Thread(new Runnable() {
				@Override
				public void run() {
					syncContent = Utilities.getContent(mBrowser.getContext(), url, new LogListener() {
						@Override
						public void run(Context context, Browser browser, int type, String message) {
							mBrowser.exception(RexseeUrl.class.getName() + ".getContent", message);
						}
					}, encoding);
					if (syncContent == null) syncContent = RexseeApplication.EXCEPTION_PREFIX;
				}
			}).start();
			while (syncContent == null) {
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
				}
			}
			mBrowser.progressDialog.hide();
			return syncContent;
		} else if (scheme.equals("file")) {
			try {
				File file = new File(uri.getPath());
				FileInputStream fileInputStream;
				fileInputStream = new FileInputStream(file);
				byte[] buffer = new byte[fileInputStream.available()];
				fileInputStream.read(buffer);
				return new String(buffer, encoding);
			} catch (Exception e) {
				return null;
			}
		} else {
			return null;
		}
	}
	public String formatTextFile_(String url, String code) {
		code = code.replaceAll("&", "&amp;");
		code = code.replaceAll("<", "&lt;");
		code = code.replaceAll(">", "&gt;");
		code = code.replaceAll("\r\n", "<br>");
		code = code.replaceAll("\n", "<br>");
		code = code.replaceAll(" ", "&nbsp;");
		code = code.replaceAll("\t", "&nbsp;&nbsp;&nbsp;&nbsp;");
		code = "<HTML><HEAD><TITLE>" + url + "</TITLE></HEAD><BODY style='word-break:break-all;'>" + code + "</BODY></HTML>";
		return code;
	}
	public String formatXmlFile_(String url, String code) {
		String rtn = "";
		XmlPullParserFactory factory;
		try {
			factory = XmlPullParserFactory.newInstance();
		} catch (XmlPullParserException e) {
			return "Exception on getting XmlPullParserFactory:" + e.getLocalizedMessage();
		}
		factory.setNamespaceAware(true);
		XmlPullParser xpp;
		try {
			xpp = factory.newPullParser();
			//xpp.setFeature("http://xmlpull.org/v1/doc/features.html#xml-roundtrip", true);
		} catch (XmlPullParserException e) {
			return "Exception on getting XmlPullParser:" + e.getLocalizedMessage();
		}
		try {
			xpp.setInput(new StringReader(code));
		} catch (XmlPullParserException e) {
			return "Exception on setInput of XmlPullParser:" + e.getLocalizedMessage();
		}
		int eventType;
		try {
			eventType = xpp.getEventType();
		} catch (XmlPullParserException e) {
			return "Exception on getEventType of XmlPullParser:" + e.getLocalizedMessage();
		}
		int uniqueID = 0;
		while (eventType != XmlPullParser.END_DOCUMENT) {
			if (eventType == XmlPullParser.START_DOCUMENT) {
				//Do nothing
			} else if (eventType == XmlPullParser.END_DOCUMENT) {
				//Do nothing
			} else if (eventType == XmlPullParser.START_TAG) {
				String tagName = xpp.getName();
				int count = xpp.getAttributeCount();
				String line = "&lt;" + tagName;
				for (int i = 0; i < count; i++) {
					String name = (xpp.getAttributePrefix(i) == null) ? xpp.getAttributeName(i) : xpp.getAttributePrefix(i) + ":" + xpp.getAttributeName(i);
					String value = xpp.getAttributeValue(i);
					line += " " + name + "=\"" + value + "\"";
				}
				line += "&gt;";
				String padding = "";
				if (xpp.getDepth() > 1) {
					padding = "padding-left:30px;";
				}
				rtn += "<div id='title_" + uniqueID + "' style='" + padding + "white-space:nowrap;display:none;' onclick=\"this.style.display='none';document.getElementById('content_" + uniqueID + "').style.display='';\"><span style='color:red;'>+&lt;" + tagName + "&gt;</span></div>";
				rtn += "<div id='content_" + uniqueID + "' style='" + padding + "white-space:nowrap;'><span style='color:red;' onclick=\"document.getElementById('content_" + uniqueID + "').style.display='none';document.getElementById('title_" + uniqueID + "').style.display='';\">" + line + "</span>";
				uniqueID++;
			} else if (eventType == XmlPullParser.END_TAG) {
				String line = "&lt;/" + xpp.getName() + "&gt;";
				rtn += line + "</div>";
			} else if (eventType == XmlPullParser.TEXT) {
				String line = xpp.getText();
				rtn += "<span style='white-space:normal;word-break:break-all;'>" + line + "</span>";
			} else if (eventType == XmlPullParser.CDSECT) {
				String line = xpp.getText();
				line = line.replaceAll("&", "&amp;");
				line = line.replaceAll("<", "&lt;");
				line = line.replaceAll(">", "&gt;");
				line = "&lt;!CDATA[" + line + "]]&gt;";
				rtn += "<span style='white-space:normal;word-break:break-all;'>" + line + "</span>";
			}
			try {
				eventType = xpp.nextToken();
			} catch (Exception e) {
				rtn += "<div style='border:1px solid red; padding:5px; color:red;font-weight:bold;'>Exception on parsing tag:" + e.getLocalizedMessage() + "</div>";
			}
		}
		Object versionObject = xpp.getProperty("http://xmlpull.org/v1/doc/properties.html#xmldecl-version");
		Object standaloneObject = xpp.getProperty("http://xmlpull.org/v1/doc/features.html#xmldecl-standalone");
		String encodingObject = xpp.getInputEncoding();
		String xml = "";
		if (versionObject != null || standaloneObject != null || encodingObject != null) {
			String version = (versionObject == null) ? "" : " version=\"" + versionObject + "\"";
			String standalone = (standaloneObject == null) ? "" : " standalone=\"" + (((Boolean) standaloneObject) ? "true" : "false") + "\"";
			String encoding = (encodingObject == null) ? "" : " encoding=\"" + encodingObject + "\"";
			xml = "<div style='white-space:nowrap;'>&lt;?xml" + version + standalone + encoding + "?&gt;</div>";
		}
		rtn = "<HTML><HEAD><TITLE>" + url + "</TITLE></HEAD><BODY>" + xml + rtn + "</BODY></HTML>";
		return rtn;
	}

	public static String cleanUrl_(String url) {
		try {
			String root = getRoot_(url);
			if (root == null) return url;
			if (url.length() <= root.length()) return url;
			String path = url.substring(root.length());
			String[] tmp = path.split("/");
			Stack<String> stack = new Stack<String>();
			for (int i = 0; i < tmp.length; i++) {
				if (tmp[i] == null) tmp[i] = "";
				String s = tmp[i].trim();
				if (s.equals(".")) {
					continue;
				} else if (s.equals("..")) {
					if (stack.size() > 0) {
						stack.pop();
					}
				} else {
					stack.push(tmp[i]);
				}
			}
			String rtn = "";
			for (int i = 0; i < stack.size(); i++) {
				if (i > 0) rtn += "/";
				rtn += stack.elementAt(i);
			}
			rtn = root + rtn;
			return rtn;
		} catch (Exception e) {
			return url;
		}
	}
	private static String getRoot_(String url) {
		String low = url.toLowerCase();
		if (low.startsWith("http://") || low.startsWith("https://")) {
			Uri uri = Uri.parse(url);
			return (uri.getPort() < 0) ? uri.getScheme() + "://" + uri.getHost() + "/" : uri.getScheme() + "://" + uri.getHost() + ":" + uri.getPort() + "/";
		} else if (low.startsWith(ASSET_URL)) {
			return ASSET_URL;
		} else if (low.startsWith("file://")) {
			return "file:///";
		} else {
			return null;
		}
	}
	private static String getPath_(String url) {
		String low = url.toLowerCase();
		if (low.startsWith("http://") || low.startsWith("https://") || low.startsWith("file://")) {
			if (url.contains("?")) url = url.substring(0, url.indexOf("?"));
			return url.substring(0, url.lastIndexOf("/")) + "/";
		} else {
			return null;
		}
	}

	//JavaScript Interface

	public String list() {
		String rtn = "[";
		for (int i = 0; i < listeners.size(); i++) {
			if (i != 0) rtn += ",";
			rtn += "\"" + listeners.get(i).url + "\"";
		}
		rtn += "]";
		return rtn;
	}

	public String getAbsoluteUrl(String path) {
		if (path == null) return null;
		if (mBrowser.history.getCurrentItem() != null) {
			return getAbsoluteUrl(mBrowser.history.getCurrentItem().getUrl(), path);
		} else {
			if (mBrowser.parentBrowser != null && mBrowser.parentBrowser.history.getCurrentItem() != null) {
				return getAbsoluteUrl(mBrowser.parentBrowser.history.getCurrentItem().getUrl(), path);
			} else {
				return path;
			}
		}
	}
	public static String getAbsoluteUrl(String baseUrl, String url) {
		if (url == null || baseUrl == null) return null;
		if (!url.contains(":")) {
			String root = getRoot_(baseUrl);
			String path = getPath_(baseUrl);
			if (root != null && path != null) {
				url = (url.startsWith("/")) ? root + url.substring(1) : path + url;
			}
		}
		return (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://")) ? cleanUrl_(url) : url;
		//return url;
	}
	public String cleanUrl(String url) {
		return cleanUrl_(url);
	}
	public String getRoot() {
		return (mBrowser.history.getCurrentItem() != null) ? getRoot_(mBrowser.history.getCurrentItem().getUrl()) : null;
	}
	public String getRoot(String url) {
		return getRoot_(url);
	}
	public String getPath() {
		return (mBrowser.history.getCurrentItem() != null) ? getPath_(mBrowser.history.getCurrentItem().getUrl()) : null;
	}
	public String getPath(String url) {
		return getPath_(url);
	}
	public String getScheme() {
		return getScheme(mBrowser.history.getCurrentItem().getUrl());
	}
	public String getScheme(String url) {
		Uri uri = Uri.parse(url);
		return uri.getScheme();
	}
	public String getHost() {
		return getHost(mBrowser.history.getCurrentItem().getUrl());
	}
	public String getHost(String url) {
		Uri uri = Uri.parse(url);
		return uri.getHost();
	}
	public String getFileName() {
		return getFileName(mBrowser.history.getCurrentItem().getUrl());
	}
	public String getFileName(String url) {
		return url.substring(url.lastIndexOf("/") + 1);
	}
	public String getFileExtension() {
		return getFileExtension(mBrowser.history.getCurrentItem().getUrl());
	}
	public String getFileExtension(String url) {
		return url.substring(url.lastIndexOf(".") + 1);
	}

}
